/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
/*
 * Topology Viewer
 */

package cgl.narada.topology.viewer;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

/**
 * This class encapsulates all the activities that happen on the nodes. It is
 * responsible for addition of a new node, deletion of an existing node, 
 * addition of a link, deletion of a link, zooming, recentering, drawing whole
 * picture on screen etc.
 */
public class NodeManagement {
    public static int INVALID_VALUE = -1;
    public double ZOOM_IN_FACTOR = 2.0;
    public double ZOOM_OUT_FACTOR = 2.0;

    private int centerX;
    private int centerY;
    private double radius;
    private int limitX1;
    private int limitX2;
    private int limitY1;
    private int limitY2;
    private Node currentNode;
    private Graphics gi;

    public VirtualNode virtualNode;
    public LinkManagement linkManagement;
    public List deletedNodes;
    private String countInformation, nodeCountInformation;

    /**
     * Only constructor
     */
    public NodeManagement() {
	virtualNode = new VirtualNode();
	linkManagement = new LinkManagement();
	nodeCountInformation = new String();
    }

    /**
     * Sets Initial positions for the view.
     * @param centerX X-coordinate of the center of the screen.
     * @param centerY Y-coordinate of the center of the screen.
     * @param  limitX1 lower limit for X-coordinate
     * @param  limitY1 lower limit for Y-coordinate
     * @param  limitX2 upper limit for X-coordinate
     * @param  limitY2 upper limit for Y-coordinate
     */
    public void setInitialPositions(int centerX, int centerY,
				    int limitX1, int limitY1, int limitX2,
				    int limitY2) {
	this.centerX = centerX;
	this.centerY = centerY;
	this.limitX1 = limitX1;
	this.limitY1 = limitY1;
	this.limitX2 = limitX2;
	this.limitY2 = limitY2;
	// Depending upon width and height of the screen, decide the radius.
	if ((limitX2 - limitX1) > (limitY2 - limitY1)) {
	    radius = (limitY2 - limitY1) * 0.5;
	} else {
	    radius = (limitX2 - limitX1) * 0.5;
	}
	/*
	System.out.println("centerX = " + centerX +
			   "centerY = " + centerY +
			   "radius = " + radius);
	*/
	Position p = new Position(centerX, centerY, radius, Color.white);
	virtualNode.nodeLocation = p;
	virtualNode.repositionSuperSuperClusters();
    }

    public void setGraphics(Graphics g) {
	this.gi = g;
    }

    /**
     * Adds a node into the system.
     * @param nodeAddress <code>String</code> representation of the node
     *        address.
     */
    public synchronized void addNode(String nodeAddress) {
	// Tokenize the string and separate all the ids
	tokenizeNodeAddress(nodeAddress);
	try {
	    virtualNode.insertSuperSuperCluster(currentNode.parentSSClusterId,
						currentNode.parentSClusterId,
						currentNode.parentClusterId,
						currentNode.nodeId);
	} catch (TopologyException te) {
	    System.out.println("EXCEPTION");
	    System.out.println(te.explanation);
	}
	updateNodeCountInformation();
    }

    /**
     * Deletes a node into the system.
     * @param nodeAddress <code>String</code> representation of the node
     *        address.
     */
    public synchronized void deleteNode(String nodeAddress) {
	// Tokenize the string and separate all the ids
	tokenizeNodeAddress(nodeAddress);
	deletedNodes = new ArrayList();
	try {
	    virtualNode.deleteSuperSuperCluster(currentNode.parentSSClusterId,
						currentNode.parentSClusterId,
						currentNode.parentClusterId,
						currentNode.nodeId,
						deletedNodes);
	    // after deleting the nodes, delete their occurances from the list
	    // maintaining links
	    Iterator itDeletedNodes = deletedNodes.iterator();
	    while (itDeletedNodes.hasNext()) {
		Node n = (Node) itDeletedNodes.next();
		linkManagement.deleteAllLinksForNode(n);
	    }
	}  catch (TopologyException te) {
	    System.out.println("EXCEPTION");
	    System.out.println(te.explanation);
	}
	updateNodeCountInformation();
    }

    /** 
     * Adds a single link into the system.
     * @param  nodeAddress1 address of one end node.
     * @param  nodeAddress2 address of other end node.
     * @param  linkId id of a link to be inserted.
     * @param  linkType <code>String</code> representing the link type.
     * @throws TopologyException if one of the end nodes is absent while
     *         insertion
     */
    public synchronized void addLink(String nodeAddress1, String nodeAddress2, 
			int linkId, String linkType)
	throws TopologyException {
	// Tokenize first node address
	tokenizeNodeAddress(nodeAddress1);
	Node n1 = virtualNode.getParticularNode(currentNode.parentSSClusterId,
						currentNode.parentSClusterId,
						currentNode.parentClusterId,
						currentNode.nodeId);
	// Tokenize second node address
	tokenizeNodeAddress(nodeAddress2);
	Node n2 = virtualNode.getParticularNode(currentNode.parentSSClusterId,
						currentNode.parentSClusterId,
						currentNode.parentClusterId,
						currentNode.nodeId);
	if ((n1 == null) || (n2 == null)) {
	    throw new TopologyException("one of the nodes absent while"
					+ " adding a new link");
	} else {
	    linkManagement.addLink(n1, n2, linkId, linkType);
	}
    }  
    
    /**
     * Deletes a particular link from the system.
     * @param  nodeAddress1 address of one end node.
     * @param  nodeAddress2 address of other end node.
     * @param  linkId id of a link to be deleted.
     */
    public synchronized void deleteLink(String nodeAddress1, 
					String nodeAddress2, int linkId) {
	// Tokenize first node address
	tokenizeNodeAddress(nodeAddress1);
	Node n1 = virtualNode.getParticularNode(currentNode.parentSSClusterId,
						currentNode.parentSClusterId,
						currentNode.parentClusterId,
						currentNode.nodeId);
	// Tokenize second node address
	tokenizeNodeAddress(nodeAddress2);
	Node n2 = virtualNode.getParticularNode(currentNode.parentSSClusterId,
						currentNode.parentSClusterId,
						currentNode.parentClusterId,
						currentNode.nodeId);
	
	try {
	    if ((n1 != null) && (n2 != null)) {
		linkManagement.deleteLink(n1, n2, linkId);
	    }
	} catch(TopologyException te) {
	    System.out.println("EXCEPTION");
	    System.out.println(te.explanation);
	}
    }

    public synchronized void addClientToNode(String nodeAddress, 
					     int clientId,
					     String clientType) 
    {
	tokenizeNodeAddress(nodeAddress);
	Node n = virtualNode.getParticularNode(currentNode.parentSSClusterId,
					       currentNode.parentSClusterId,
					       currentNode.parentClusterId,
					       currentNode.nodeId);
	n.addClient(clientId, clientType);
    }

    public synchronized void deleteClientFromNode(String nodeAddress, 
						  int clientId) {
	tokenizeNodeAddress(nodeAddress);
	Node n = virtualNode.getParticularNode(currentNode.parentSSClusterId,
					       currentNode.parentSClusterId,
					       currentNode.parentClusterId,
					       currentNode.nodeId);
	try {
	    n.deleteClient(clientId);
	} catch(TopologyException te) {
	    System.out.println("EXCEPTION");
	    System.out.println(te.explanation);
	}
    }

    public synchronized void deleteAllClients(String nodeAddress) {
	tokenizeNodeAddress(nodeAddress);
	Node n = virtualNode.getParticularNode(currentNode.parentSSClusterId,
					       currentNode.parentSClusterId,
					       currentNode.parentClusterId,
					       currentNode.nodeId);
	n.deleteAllClients();
    }

    public synchronized void addTopicToClient(String nodeAddress, int clientId,
				 String type) {
	tokenizeNodeAddress(nodeAddress);
	Node n = virtualNode.getParticularNode(currentNode.parentSSClusterId,
					       currentNode.parentSClusterId,
					       currentNode.parentClusterId,
					       currentNode.nodeId);
	try {
	    n.addTopic(clientId, type);
	} catch(TopologyException te) {
	    System.out.println("EXCPETION");
	    System.out.println(te.explanation);
	}
    }

    public synchronized void deleteTopicFromClient(String nodeAddress,
						   int clientId, 
						   String topic) {
	tokenizeNodeAddress(nodeAddress);
	Node n = virtualNode.getParticularNode(currentNode.parentSSClusterId,
					       currentNode.parentSClusterId,
					       currentNode.parentClusterId,
					       currentNode.nodeId);
	try {
	    n.deleteTopic(clientId, topic);
	} catch(TopologyException te) {
	    System.out.println("EXCPETION");
	    System.out.println(te.explanation);
	}
    }

    public synchronized void deleteAllTopicsFromNode(String nodeAddress,
						     int clientId, 
						     String topic) {
	tokenizeNodeAddress(nodeAddress);
	Node n = virtualNode.getParticularNode(currentNode.parentSSClusterId,
					       currentNode.parentSClusterId,
					       currentNode.parentClusterId,
					       currentNode.nodeId);
	try {
	    n.deleteAllTopics(clientId);
	} catch(TopologyException te) {
	    System.out.println("EXCPETION");
	    System.out.println(te.explanation);
	}
    }


    public List getAllClientsForNode(Node n) {
	return n.getAllClients();
    }

    /**
     * Updates the variable <code>nodeCountInformation</code>
     */
    private void updateNodeCountInformation() {
	nodeCountInformation = virtualNode.getAllCounts();
    }

    /**
     * Returns count information consisting of node counts and link count.
     * @return <code>String</code> representing all the counts in system.
     */
    public String getCountInformation() {
	return (nodeCountInformation + ",  links = " + 
		linkManagement.getLinkCount());
    }

    // returns list of nodes, and list of all links
    public synchronized List getSnapShot(List allLinks) {
	List nodes = new ArrayList();
	Iterator itSSClusters = virtualNode.superSuperClusters.iterator();
	while (itSSClusters.hasNext()) {
	    SSCluster ssc = (SSCluster) itSSClusters.next();
	    Iterator itSClusters = ssc.superClusters.iterator();
	    while (itSClusters.hasNext()) {
		SCluster sc = (SCluster) itSClusters.next();
		Iterator itClusters = sc.clusters.iterator();
		while (itClusters.hasNext()) {
		    Cluster c = (Cluster) itClusters.next();
		    Iterator itNode = c.nodes.iterator();
		    while (itNode.hasNext()) {
			Node n = (Node) itNode.next();
			nodes.add(n.getNodeAddress());
		    }
		}
	    }
	}
	Iterator itLinks = linkManagement.links.iterator();
	while (itLinks.hasNext()) {
	    LinkDetails link = (LinkDetails) itLinks.next();
	    LinkDetailsTemp ldtemp = 
		new LinkDetailsTemp(link.connectedNode1.getNodeAddress(),
				    link.connectedNode2.getNodeAddress());
	    Iterator itPairLinks = link.linksForOneNode.iterator();
	    while (itPairLinks.hasNext()) {
		Link l = (Link) itPairLinks.next();
		ldtemp.insertLinkForNode(l.linkId, l.linkType);
	    }
	    allLinks.add(ldtemp);
	}
	return nodes;
    }


    /** 
     * Returns <code>Node</code> object closest to specified coordinates.
     * @param  x X-coordinate of mouse pointer.
     * @param  y Y-coordinate of mouse pointer.
     * @return <code>Node</code> 
     */
    public Node getNodeAtParticularCoordinates(int x, int y) {
	return virtualNode.getNodeAtParticularCoordinates(x, y);
    }

    /**
     * Tokenizes the string address of a node into component ids. 
     * @param nodeAddress address of a node
     */
    private void tokenizeNodeAddress(String nodeAddress) {
	StringTokenizer st = new StringTokenizer(nodeAddress, ". ");
	int i = 4;
	String id;
	currentNode = new Node();
	while ((st.hasMoreTokens()) && (i != 0)) {
	    if (st.hasMoreTokens()) {
		id = st.nextToken();
	    } else {
		id = String.valueOf(INVALID_VALUE);
	    }
	    if (i == 4) {
		currentNode.parentSSClusterId = Integer.parseInt(id);
	    } else if (i == 3) {
		currentNode.parentSClusterId = Integer.parseInt(id);
	    } else if (i == 2) {
		currentNode.parentClusterId = Integer.parseInt(id);
	    } else if (i == 1) {
		currentNode.nodeId = Integer.parseInt(id);
	    }
	    i = i - 1;
	}
    }
    
    /**
     * Draws the screen view. First draws all the nodes and then links.
     */
    public void drawView() {
	// draw nodes
	virtualNode.drawVirtualNode(gi, limitX1, limitY1, limitX2, limitY2);
	// draw links
	linkManagement.drawAllLinks(gi);
    }

    /**
     * Sets <code>ZOOM_IN_FACTOR</code>
     * @param zoomInFactor
     */
    public void setZoomInFactor(int zoomInFactor) {
	ZOOM_IN_FACTOR = zoomInFactor;
    }

    /**
     * Sets <code>ZOOM_OUT_FACTOR</code>
     * @param zoomOutFactor
     */
    public void setZoomOutFactor(int zoomOutFactor) {
	ZOOM_OUT_FACTOR = zoomOutFactor;
    }

    /**
     * zooms into the picture. Modifies center and radius of 
     * <code>virtualNode</code>. Repositions all the components.
     * @param screenCenterX X-coordinate of center of the screen.
     * @param screenCenterY Y-coordinate of the center of the screen. 
     */
    public void zoomIn(int screenCenterX, int screenCenterY) {
	int shiftX = screenCenterX - this.centerX;
	int shiftY = screenCenterY - this.centerY;
	this.centerX = this.centerX - (int)((ZOOM_IN_FACTOR - 1) * shiftX);
	this.centerY = this.centerY - (int)((ZOOM_IN_FACTOR - 1) * shiftY);
	virtualNode.nodeLocation.setCenterX(this.centerX);
	virtualNode.nodeLocation.setCenterY(this.centerY);
	virtualNode.nodeLocation.setRadius(virtualNode.nodeLocation.getRadius()
					   * ZOOM_IN_FACTOR);
	virtualNode.repositionSuperSuperClusters();
    }

    /**
     * zooms out from the picture. Modifies center and radius of 
     * <code>virtualNode</code>. Repositions all the components.
     * @param screenCenterX X-coordinate of center of the screen.
     * @param screenCenterY Y-coordinate of the center of the screen. 
     */
    public void zoomOut(int screenCenterX, int screenCenterY) {
	int shiftX = (int) ((screenCenterX - this.centerX) 
	    * ((ZOOM_OUT_FACTOR - 1) / ZOOM_OUT_FACTOR));
	int shiftY = (int) ((screenCenterY - this.centerY) 
	    * ((ZOOM_OUT_FACTOR - 1) / ZOOM_OUT_FACTOR));
	this.centerX = this.centerX + shiftX;
	this.centerY = this.centerY + shiftY;
	virtualNode.nodeLocation.setCenterX(this.centerX);
	virtualNode.nodeLocation.setCenterY(this.centerY);
	virtualNode.nodeLocation.setRadius(virtualNode.nodeLocation.getRadius()
					   / ZOOM_OUT_FACTOR);
	virtualNode.repositionSuperSuperClusters();
    }

    /**
     * Translates the view. New center becomes the location where mouse is
     * clicked. It changes center of a <code>virtualNode</code> and 
     * repositions all the components in the system.
     * @param newCenterX X-coordinate of a  new center for the view.
     * @param newCenterY Y-coordinate of a new center for the view.
     * @param screenCenterX X-coordinate of a screen center.
     * @param screenCenterY Y-coordinate of a screen center.
     */
    public void recenter(int newCenterX, int newCenterY, int screenCenterX,
			 int screenCenterY) {
	// center of the virtualNode gets changed.
	this.centerX = this.centerX - (newCenterX - screenCenterX);
	this.centerY = this.centerY - (newCenterY - screenCenterY);
	virtualNode.nodeLocation.setCenterX(this.centerX);
	virtualNode.nodeLocation.setCenterY(this.centerY);
	virtualNode.repositionSuperSuperClusters();
    }
};
